// Copyright Up and Running var mongoose = require('mongoose'), path = require('path'), Schema = mongoose.Schema, Q = require('q'), upload = require(path.resolve(__dirname, 'fileUpload')), settings = require(path.resolve(__dirname, '..', 'config', 'settings')), tourUtil = require(path.resolve(__dirname, '..', 'util', 'tourUtil')); // partial definition of schemas // subdocument which is a part of tour schema var iconSchema = mongoose.Schema({ originalname: {type: String, required: '{PATH} is required!' }, name: {type: String, required: '{PATH} is required!' }, mimetype: String, extension: String, size: Number, width: {type: Number, required: '{PATH} is required!' }, height: {type: Number, required: '{PATH} is required!' }, dir: {type: String, required: '{PATH} is required!' } }); var Icon = mongoose.model('Icon', iconSchema); // subdocument which is a part of tour schema var groupSchema = mongoose.Schema({ name: {type: String, required: '{PATH} is required!' } }); var Group = mongoose.model('Group', groupSchema); // main schema var tourSchema = mongoose.Schema({ google_id: {type: String, required: '{PATH} is required!' }, place_id: String, reference: String, type: {type: String, required: '{PATH} is required!' }, created: { type: Date, required: true, default: Date.now }, updated: { type: Date, required: true, default: Date.now }, name: {type: String, required: '{PATH} is required!' }, unlocked: Boolean, lat: Number, long: Number, pano_id: String, heading: Number, zoom: Number, height: Number, width: Number, // subdocuments scenes: [sceneSchema], hotspots: [hotspotSchema], groups: [ { type: Schema.Types.ObjectId, ref: 'Group' } ] }); var Tour = mongoose.model('Tour', tourSchema); // mongoose queries for storing data // note here we demonstrate only few of them var CreateTour = function (data) { var getTourDefer = Q.defer(), tourObj = {}; // check if tour exists GetTourByID(data.id).then(function (tour) { if (tour) { getTourDefer.resolve(tour); } else { // tour definition tourObj = { google_id: data.id, place_id: data.place_id, reference: data.reference, type: data.type, name: data.name, unlocked: false, lat: data.geometry.location.lat, long: data.geometry.location.lng, pano_id: data.pano_id, heading: data.additional_info.heading, zoom: data.additional_info.zoom, height: data.additional_info.height, width: data.additional_info.width, scenes: [], hotspots: [], groups: [] }; Tour.create(tourObj, function (err, tour) { if (err) return getTourDefer.reject(err); // when sending results back, scenes object is sorted by multiple criterias (group, weight, date) tour = tourUtil.scenes.sort(tour); getTourDefer.resolve(tour); }); } }, function (err) { getTourDefer.reject(err); }); return getTourDefer.promise; }; // Retrieves tour by ID var GetTourByID = function (id) { var getTourDefer = Q.defer(); if (id) { Tour.findOne({google_id: id}) // for retrieving all data from tour model, including groups, audio, etc. .populate('scenes.group') .populate('scenes.audio') .populate('hotspots.marker.icon') .populate('hotspots.content.audio') .exec(function (err, tour) { if (err) return getTourDefer.reject(err); tour = tourUtil.scenes.sort(tour); getTourDefer.resolve(tour); }); } else { return getTourDefer.reject({ message: 'No matching tour' }); } return getTourDefer.promise; }; // Create label/scene for tour by ID var CreateLabel = function (id, data) { // label definition var getLabelsDefer = Q.defer(), scene = { name: data.label, group: data.group, position: { pano_id: data.pano_id, pitch: data.pitch, zoom: data.zoom, heading: data.heading, lat: data.lat, long: data.lng } }, fileTypes = [ { type: Audio, max: settings.files.audio.maxSize, mime: settings.files.audio.type } ]; // first we upload file to exact location on the file system upload.UploadFiles(data, fileTypes).then(function (data) { if (data[0].attrs._id) { scene.audio = data[0].attrs._id; } // then we find the tour, which is a parent schema for label subdocument Tour.findOne({google_id: id}) .populate('scenes.group') .populate('scenes.audio') .exec(function (err, tour) { if (err) return getLabelsDefer.reject(err); if (!tour.scenes) tour.scenes = []; tour.scenes.push(scene); // after we update tour object we are saving entire tour schema // note that we could use mongoose findAndUpdate but in that case no validation of data will be triggered tour.save(function (err) { if (err) return getLabelsDefer.reject(err); Tour.findOne({google_id: id}) .populate('scenes.group') .populate('scenes.audio') .exec(function (err, tour) { if (err) return getLabelsDefer.reject(err); tour = tourUtil.scenes.sort(tour); getLabelsDefer.resolve(tour.scenes); }); }); }); }, function (err) { getLabelsDefer.reject(err); }); return getLabelsDefer.promise; }; // Delete tour label/scene by ID var DeleteLabelByID = function (tourID, id) { var getLabelsDefer = Q.defer(), scene = {}; // find tour which contains desired label Tour.findOne({ google_id: tourID, 'scenes._id': id }, 'scenes.$') .populate('scenes.audio') .exec(function (err, tour) { if (err) return getLabelsDefer.reject(err); scene = tour.scenes[0]; // delete file from file system upload.DeleteFile(Audio, scene.audio).then(function () { // retrieve updated tour without deleted label Tour.findOneAndUpdate( { google_id: tourID }, { $pull: { scenes: {_id: id} } }) .populate('scenes.group') .populate('scenest.audio') .exec(function (err, tour) { if (err) return getLabelsDefer.reject(err); tour = tourUtil.scenes.sort(tour); getLabelsDefer.resolve(tour.scenes); }); }, function (err) { getLabelsDefer.reject(err); }); }); return getLabelsDefer.promise; }; // make these methods public module.exports = { // tours CreateTour: CreateTour, GetTourByID: GetTourByID, // scenes CreateLabel: CreateLabel, DeleteLabelByID: DeleteLabelByID };